﻿using SharpDepend.Datatypes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace SharpDepend.WPF
{
    /// <summary>
    /// Reference: http://www.jarloo.com/c-udp-multicasting-tutorial/
    /// </summary>
    internal class MulticastManager : ISocketMulticastManager
    {
        class SocketMulticast : ISocketMulticast
        {
            private UdpClient udpclient;
            private IPEndPoint localEp;
            private IPEndPoint remoteep;

            public BoolResult CreateMulticastSender(string ip, int port)
            {
                try
                {
                    if (udpclient != null)
                    {
                        throw new NotSupportedException("Multicast can not be a sender and listener at the same time.");
                    }

                    udpclient = new UdpClient();

                    IPAddress multicastaddress = IPAddress.Parse(ip);
                    udpclient.JoinMulticastGroup(multicastaddress);
                    remoteep = new IPEndPoint(multicastaddress, port);

                    return BoolResult.Succeded;
                }
                catch (Exception error)
                {
                    return new BoolResult(error);
                }
            }

            public BoolResult CreateMulticastListener(string ip, int port)
            {
                try
                {
                    if (udpclient != null)
                    {
                        throw new NotSupportedException("Multicast can not be a sender and listener at the same time.");
                    }

                    udpclient = new UdpClient();

                    // Allows multiplie listeners.
                    udpclient.ExclusiveAddressUse = false;
                    IPEndPoint localEp = new IPEndPoint(IPAddress.Any, port);
                    udpclient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
                    udpclient.ExclusiveAddressUse = false;

                    udpclient.Client.Bind(localEp);

                    IPAddress multicastaddress = IPAddress.Parse(ip);
                    udpclient.JoinMulticastGroup(multicastaddress);

                    return BoolResult.Succeded;
                }
                catch (Exception error)
                {
                    return new BoolResult(error);
                }
            }

            public void Write(byte[] data, int length)
            {
                udpclient.Send(data, length, remoteep);
            }

            [System.Diagnostics.DebuggerStepThrough]
            public void Read(MulticastListenerResult result)
            {
                try
                {
                    result(udpclient.Receive(ref localEp));
                }
                catch (Exception)
                {
                }
            }

            public void Dispose()
            {
                udpclient.Close();
                udpclient = null;
            }
        }

        public ISocketMulticast CreateMulticast()
        {
            return new SocketMulticast();
        }
    }
}
